home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Night Owl 8
/
Night Owl CD-ROM (NOPV8) (Night Owl Publisher) (1993).ISO
/
017a
/
binutils.arj
/
ROBOTUSS.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-03-27
|
19KB
|
600 lines
/* Convert COFF-format object file to BSD format.
Used for converting the system libraries so GNU ld can link them.
Copyright (C) 1988 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/*
** Robotussin - convert COFF format object files to BSD format.
**
** written by Jeff Lewis, donated to the Free Software Foundation.
**
** BUGS:
** Should do more to verify that the input COFF file meets our
** expectations.
** On machines where the structure of the COFF data in the file does not
** match the structure of the COFF data declared (when, for example
** sizeof (struct filhdr) != FILHSZ), this program will fail. (Don't
** ask me why this is ever allowed to come about). Accessor functions/
** macros that painstakingly extract the data out of the file and stuff
** it in the memory struct should be written to fix this on such machines.
**
** CAVEATS:
** This program cannot claim correctness, however, it does appear
** to work on my fairly vanilla Sys5r2 machine. Someone with the time
** and a fine tooth comb (not to mention some documentation on COFF)
** should correct this!
*/
#ifndef COFF_ENCAPSULATE
#define COFF_ENCAPSULATE
#endif
/* Customization for particular machines. */
#ifdef i386
#define INPUT_MAGIC I386MAGIC
#else /* not i386 */
#if defined (m68k) || defined (mc68000)
#define INPUT_MAGIC MC68MAGIC
#endif
#endif /* not i386 */
#include <stdio.h>
#include <varargs.h>
#include <fcntl.h>
#include "a.out.encap.h"
#define N_ABSOLUTE N_ABS /* N_ABS will be redefined in syms.h */
#undef N_ABS
#include <filehdr.h>
#include <aouthdr.h>
#include <scnhdr.h>
#include <syms.h>
#include <reloc.h>
/* Because of struct alignment on dwords sizeof (struct syment) is different
than the syments stored in the file. Therefore, we must kludge: */
#define sizeof_syment (SYMESZ)
#define sizeof_reloc (RELSZ)
#define sizeof_section (SCNHSZ)
#define sizeof_coff_header (FILHSZ)
/*
Without the following, compiling this is a pain on a BSD4.3 Vax.
Though that might not seem useful, the compatibility of byte order
between a Vax and a 386 can come in handy for cross-debugging
*/
#ifdef NO_VFPRINTF
#define vfprintf(fp, format, ap) _doprnt (format, ap, fp)
#endif /* not HAVE_VPRINTF */
extern long lseek ();
extern void exit ();
extern char *memcpy ();
extern int errno;
void error (), sys_error ();
static void reloc_segment ();
char *mem_alloc ();
int fd_in, fd_out; /* input and output file descriptors */
struct filehdr coff_header; /* file header from the input file */
struct exec bsd_header; /* file header for the output file */
struct syment *coff_sym_listp; /* list of symbols from the input */
int *symbol_map; /* mapping of input symbol #'s to
output symbol numbers */
char *text_and_data; /* space for text & data section data */
char *relocations; /* space for output reloc entries */
int verbose_flag; /* flag for debugging */
struct scnhdr coff_text_header; /* COFF text section header */
struct scnhdr coff_data_header; /* COFF data section header */
struct scnhdr coff_bss_header; /* COFF bss section header */
int text_sect_num; /* COFF section # for text */
int data_sect_num; /* COFF section # for data */
int bss_sect_num; /* COFF section # for bss */
int
main (argc, argv)
int argc;
char **argv;
{
int i, j;
char *coff_string_table, *bsd_string_table;
register char *pc, *pc2;
int string_table_len;
int symbol_count;
struct scnhdr section;
struct nlist name;
if (argc < 3)
error ("Usage: %s COFF-file BSD-file", argv[0]);
if (argc > 3)
verbose_flag = 1;
fd_in = open (argv[1], O_RDONLY);
if (fd_in < 0)
sys_error ("can't open %s", argv[1]);
fd_out = open (argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (fd_out < 0)
sys_error ("can't open %s", argv[2]);
/*
** Read in the file header and all section headers searching
** for text, data and bss. We note the section #'s of these
** sections for use when examining symbols.
*/
if (read (fd_in, &coff_header, sizeof_coff_header) != sizeof_coff_header)
error ("can't read file header");
if (coff_header.f_magic != INPUT_MAGIC)
error ("bad magic number in coff file\n");
lseek (fd_in, sizeof_coff_header + coff_header.f_opthdr, 0);
for (i = 1; i <= coff_header.f_nscns; ++i)
{
if (read (fd_in, §ion, sizeof_section) != sizeof_section)
error ("can't read section header #%d", i);
if (strcmp (section.s_name, _TEXT) == 0)
{
text_sect_num = i;
memcpy (&coff_text_header, §ion, sizeof section);
}
else if (strcmp (section.s_name, _DATA) == 0)
{
data_sect_num = i;
memcpy (&coff_data_header, §ion, sizeof section);
}
else if (strcmp (section.s_name, _BSS) == 0)
{
bss_sect_num = i;
memcpy (&coff_bss_header, §ion, sizeof section);
}
}
#ifdef sun386
/*
The purpose of the following kludge is to cope with the discrepancy
between coff_data_header.s_vaddr and coff_text_header.s_size; in a BSD
object file, they are assumed equal. It should be ok to use
coff_data_header.s_paddr, since it has no other apparent use.
*/
coff_data_header.s_paddr = coff_text_header.s_size;
coff_bss_header.s_paddr = coff_data_header.s_size + coff_text_header.s_size;
#endif
/*
** Pass1 thru the symbol table - count usable symbols and map
** old symbol #'s into new ones (as used by relocation
** info). We're only interested in keeping the kinds of symbols
** we'd expect to find in a BSD library object file: no debug
** symbols, file names, section definition symbols, etc.
** Section definition symbols are referenced by reloc entries
** in the COFF file, so we note their position with a negative
** symbol number indicating the section. -1 is used to flag
** symbols we're not interested in, yielding an unexpected error
** if we find any reloc entries referencing them.
*/
coff_sym_listp =
(struct syment *) mem_alloc (coff_header.f_nsyms * sizeof (struct syment));
symbol_map = (int *) mem_alloc (coff_header.f_nsyms * sizeof *symbol_map);
if (lseek (fd_in, coff_header.f_symptr, 0) < 0L)
sys_error ("can't seek to COFF symbols");
for (i = 0; i < coff_header.f_nsyms; ++i)
{
if (read (fd_in, coff_sym_listp + i, sizeof_syment) != sizeof_syment)
error ("can't read COFF symbols");
}
symbol_count = 0;
for (i = 0; i < coff_header.f_nsyms; ++i)
{
if (coff_sym_listp[i].n_scnum != N_DEBUG
&& coff_sym_listp[i].n_name[0] != '.'
&& coff_sym_listp[i].n_scnum != -1)
{
if (verbose_flag)
printf ("map %d to %d\n", i, symbol_count);
symbol_map[i] = symbol_count++;
#ifdef sun386
if (coff_sym_listp[i].n_scnum == data_sect_num)
coff_sym_listp[i].n_value -=
coff_data_header.s_vaddr - coff_data_header.s_paddr;
else if (coff_sym_listp[i].n_scnum == bss_sect_num)
coff_sym_listp[i].n_value -=
coff_bss_header.s_vaddr - coff_bss_header.s_paddr;
#endif
}
else
{
if (coff_sym_listp[i].n_sclass == C_STAT)
{
if (strcmp (coff_sym_listp[i].n_name, _TEXT) == 0)
symbol_map[i] = -N_TEXT;
else if (strcmp (coff_sym_listp[i].n_name, _DATA) == 0)
symbol_map[i] = -N_DATA;
else if (strcmp (coff_sym_listp[i].n_name, _BSS) == 0)
symbol_map[i] = -N_BSS;
else
symbol_map[i] = -1;
}
else
{
symbol_map[i] = -1;
}
}
/* skip auxillary entries */
j = coff_sym_listp[i].n_numaux;
if (j != 0)
{
if (j < 0)
error ("invalid numaux");
if (j != 1)
fprintf (stderr, "unlikely numaux value\n");
while (--j >= 0)
++i;
}
}
/* now we know enough to write the output file header */
N_SET_MAGIC (bsd_header, OMAGIC);
bsd_header.a_text = coff_text_header.s_size;
bsd_header.a_data = coff_data_header.s_size;
bsd_header.a_bss = coff_bss_header.s_size;
bsd_header.a_syms = symbol_count * sizeof (struct nlist);
bsd_header.a_entry = 0;
bsd_header.a_trsize = coff_text_header.s_nreloc * sizeof (struct relocation_info);
bsd_header.a_drsize = coff_data_header.s_nreloc * sizeof (struct relocation_info);
if (write (fd_out, &bsd_header, sizeof bsd_header) != sizeof bsd_header)
sys_error ("can't write BSD header");
/*
** Read in and save text and data sections - some data in
** these sections may need to be altered due to relocations.
*/
text_and_data = (char *) mem_alloc (coff_text_header.s_size + coff_data_header.s_size);
if (lseek (fd_in, coff_text_header.s_scnptr, 0) < 0L)
sys_error ("can't seek to text section");
if (read (fd_in, text_and_data, coff_text_header.s_size) != coff_text_header.s_size)
error ("can't read text section");
if (lseek (fd_in, coff_data_header.s_scnptr, 0) < 0L)
sys_error ("can't seek to data section");
if (read (fd_in, text_and_data + coff_text_header.s_size, coff_data_header.s_size) != coff_data_header.s_size)
error ("can't read data section");
/*
** Convert the relocation entries and do any text or data
** modifications necessary.
*/
relocations = (char *) mem_alloc (bsd_header.a_trsize + bsd_header.a_drsize);
reloc_segment (&coff_text_header, relocations);
reloc_segment (&coff_data_header, relocations + bsd_header.a_trsize);
if (write (fd_out, text_and_data, coff_text_header.s_size + coff_data_header.s_size)
!= coff_text_header.s_size + coff_data_header.s_size)
sys_error ("can't write text and data sections");
/* ZZ - are there any alignment considerations?? */
if ((coff_text_header.s_size & 1) || (coff_data_header.s_size & 1))
fprintf (stderr, "non-aligned text or data section\n");
if (write (fd_out, relocations, bsd_header.a_trsize + bsd_header.a_drsize)
!= bsd_header.a_trsize + bsd_header.a_drsize)
sys_error ("can't write relocation entries");
/*
** Second pass thru the symbol table.
** a COFF symbol entry may contain up to 8 chars of symbol name
** in the entry itself - symbol names > 8 go into the string table,
** whereas the BSD entry puts all symbol names into the string
** table.
*/
if (lseek (fd_in, coff_header.f_symptr + coff_header.f_nsyms * sizeof_syment, 0) < 0L)
error ("can't seek to string table");
i = read (fd_in, &string_table_len, sizeof string_table_len);
if (i == sizeof string_table_len)
{
coff_string_table = mem_alloc (string_table_len);
string_table_len -= sizeof string_table_len;
i = read (fd_in, coff_string_table + sizeof string_table_len, string_table_len);
if (i < 0)
error ("can't read string table");
if (i != string_table_len)
error ("truncated string table - expected %d, got %d",
string_table_len, i);
}
else
{
string_table_len = 0;
}
bsd_string_table = mem_alloc (string_table_len + coff_header.f_nsyms * (SYMNMLEN + 1));
pc = bsd_string_table + sizeof string_table_len;
for (i = 0; i < coff_header.f_nsyms; ++i)
{
if (coff_sym_listp[i].n_scnum != N_DEBUG
&& coff_sym_listp[i].n_name[0] != '.'
&& coff_sym_listp[i].n_scnum != -1)
{
if (coff_sym_listp[i].n_zeroes == 0)
{
j = pc - bsd_string_table;
#ifndef nounderscore
if (coff_sym_listp[i].n_sclass == C_EXT
|| coff_sym_listp[i].n_sclass == C_STAT)
*pc++ = '_';
#endif
pc2 = coff_string_table + coff_sym_listp[i].n_offset;
while (*pc++ = *pc2++)
/* null */ ;
name.n_un.n_strx = j;
}
else
{
pc2 = &coff_sym_listp[i].n_name[0];
j = pc - bsd_string_table;
#ifndef nounderscore
if (coff_sym_listp[i].n_sclass == C_EXT
|| coff_sym_listp[i].n_sclass == C_STAT)
*pc++ = '_';
#endif
{
int x;
for (x = 0; x < SYMNMLEN; x++)
{
if (*pc2 == 0)
break;
*pc++ = *pc2++;
}
*pc++ = 0;
}
name.n_un.n_strx = j;
}
switch (coff_sym_listp[i].n_scnum)
{
case N_ABS:
name.n_type = N_ABSOLUTE;
break;
case N_UNDEF:
name.n_type = N_UNDF;
break;
default:
if (coff_sym_listp[i].n_scnum == text_sect_num)
name.n_type = N_TEXT;
else if (coff_sym_listp[i].n_scnum == data_sect_num)
name.n_type = N_DATA;
else if (coff_sym_listp[i].n_scnum == bss_sect_num)
name.n_type = N_BSS;
break;
}
if (coff_sym_listp[i].n_sclass == C_EXT)
name.n_type |= N_EXT;
name.n_other = 0;
name.n_desc = 0;
name.n_value = coff_sym_listp[i].n_value;
if (write (fd_out, &name, sizeof name) != sizeof name)
sys_error ("can't write symbol");
}
/* skip auxillary entries */
j = coff_sym_listp[i].n_numaux;
if (j != 0)
{
while (--j >= 0)
++i;
}
}
i = *((int *) bsd_string_table) = pc - bsd_string_table;
if (write (fd_out, bsd_string_table, i) != i)
error ("can't write string table");
close (fd_in);
close (fd_out);
exit (0);
}
/*
** Convert the relocation entries and do any text or data
** modifications necessary.
*/
static void
reloc_segment (section_headerp, reloc_infop)
struct scnhdr *section_headerp;
struct relocation_info *reloc_infop;
{
struct reloc coff_reloc;
int i;
if (lseek (fd_in, section_headerp->s_relptr, 0) < 0L)
error ("can't seek to relocation entries");
for (i = 0; i < section_headerp->s_nreloc; ++i)
{
if (read (fd_in, &coff_reloc, sizeof_reloc) != sizeof_reloc)
error ("can't read relocation entry");
#ifdef sun386
coff_reloc.r_vaddr -=
section_headerp->s_vaddr - section_headerp->s_paddr;
#endif
if (verbose_flag)
printf ("vaddr = 0x%x, symndx = %d\n", coff_reloc.r_vaddr, coff_reloc.r_symndx);
/*
** The reloc references a symbol declared common, thus the
** value of the symbol holds its size (in bytes). In COFF,
** apparently this info is also put into the binary -
** BSD doesn't like this, so we subtract it out.
*/
if (coff_sym_listp[coff_reloc.r_symndx].n_scnum == N_UNDEF)
{
if (coff_sym_listp[coff_reloc.r_symndx].n_value != 0)
{
if (verbose_flag)
printf ("adjust common 0x%x (%d)\n",
coff_sym_listp[coff_reloc.r_symndx].n_value,
coff_sym_listp[coff_reloc.r_symndx].n_value);
switch (coff_reloc.r_type)
{
case R_RELBYTE:
*((char *) (text_and_data + coff_reloc.r_vaddr))
-= coff_sym_listp[coff_reloc.r_symndx].n_value;
break;
case R_RELWORD:
*((short *) (text_and_data + coff_reloc.r_vaddr))
-= coff_sym_listp[coff_reloc.r_symndx].n_value;
break;
case R_RELLONG:
case R_DIR32: /* these are the only two that really show up */
case R_PCRLONG:
*((int *) (text_and_data + coff_reloc.r_vaddr))
-= coff_sym_listp[coff_reloc.r_symndx].n_value;
break;
default:
error ("unknown relocation type 0%o", coff_reloc.r_type);
}
}
}
/*
** >= 0 means its an extern - value is the output symbol #.
** < 0 means its an intern - value is N_TEXT, N_DATA or N_BSS.
*/
if (symbol_map[coff_reloc.r_symndx] >= 0)
{
reloc_infop->r_symbolnum = symbol_map[coff_reloc.r_symndx];
reloc_infop->r_extern = 1;
}
else
{
if (symbol_map[coff_reloc.r_symndx] == -1)
error ("Oops! possible bug - reloc reference to ignored symbol");
#ifdef sun386
{
int offset=0;
switch (-symbol_map[coff_reloc.r_symndx])
{
case N_DATA:
offset = coff_data_header.s_vaddr - coff_data_header.s_paddr;
break;
case N_BSS:
offset = coff_bss_header.s_vaddr - coff_bss_header.s_paddr;
break;
}
switch (coff_reloc.r_type)
{
case R_RELBYTE:
*((char *) (text_and_data + coff_reloc.r_vaddr)) -= offset;
break;
case R_RELWORD:
*((short *) (text_and_data + coff_reloc.r_vaddr)) -= offset;
break;
case R_RELLONG:
case R_DIR32:
case R_PCRLONG:
*((int *) (text_and_data + coff_reloc.r_vaddr)) -= offset;
break;
default:
error ("unknown relocation type 0%o", coff_reloc.r_type);
}
}
#endif
reloc_infop->r_symbolnum = -symbol_map[coff_reloc.r_symndx];
reloc_infop->r_extern = 0;
}
/*
** COFF address includes the section address - BSD doesn't, so
** subtract it out.
*/
#ifdef sun386
coff_reloc.r_vaddr +=
section_headerp->s_vaddr - section_headerp->s_paddr;
#endif
reloc_infop->r_address = coff_reloc.r_vaddr - section_headerp->s_vaddr;
switch (coff_reloc.r_type)
{
case R_PCRLONG:
reloc_infop->r_pcrel = 1;
reloc_infop->r_length = 2; /* 4 bytes */
break;
case R_DIR32:
case R_RELLONG:
reloc_infop->r_pcrel = 0;
reloc_infop->r_length = 2;
break;
default:
error ("can't handle coff reloction type 0%o", coff_reloc.r_type);
}
if (verbose_flag)
printf ("reloc: addr = 0x%x, synum = %d\n",
reloc_infop->r_address, reloc_infop->r_symbolnum);
reloc_infop->r_pad = 0;
++reloc_infop;
}
}
void
error (format, va_alist)
char *format;
va_dcl
{
va_list args;
va_start (args);
fprintf (stderr, "robotussin: ");
vfprintf (stderr, format, args);
putc ('\n', stderr);
va_end (args);
exit (1);
}
extern char *sys_errlist[];
extern int errno;
void
sys_error (format, va_alist)
char *format;
va_dcl
{
va_list args;
va_start (args);
fprintf (stderr, "robotussin: ");
vfprintf (stderr, format, args);
fprintf (stderr, ": %s\n", sys_errlist[errno]);
va_end (args);
exit (1);
}
extern char *malloc ();
char *
mem_alloc (size)
int size;
{
char *pc;
if ((pc = malloc (size)) == NULL)
error ("memory exhausted!");
return pc;
}
/* end */